﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ShootSearch.Util
{
    public class StringHasher
    {
        /// <summary>
        /// Hash function, HashString
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static int HashString(string s)
        {
            int hash = 0;

            for (int i = 0; i < s.Length; i++)
            {
                hash += s[i];
                hash += (hash << 10);
                hash ^= (hash >> 6);
            }
            hash += (hash << 3);
            hash ^= (hash >> 11);
            hash += (hash << 15);
            return hash;
        }


        public const int RSHASH_SEED = 378551;

        /// <summary>
        /// RS Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static uint RSHash(string str)
        {
            uint hash = 0;
            uint a = 63689;
            for (int i = 0; i < str.Length; i++)
            {
                hash = hash * a + str[i];
                a *= RSHASH_SEED;
            }
            return (hash & 0x7FFFFFFF);
        }

        public const uint JSHASH_SEED = 1315423911;

        /// <summary>
        /// JS Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static uint JSHash(string str)
        {
            uint hash = JSHASH_SEED;

            for (int i = 0; i < str.Length; i++)
            {
                hash ^= ((hash << 5) + str[i] + (hash >> 2));
            }

            return (hash & 0x7FFFFFFF);
        }



        /// <summary>
        /// P. J. Weinberger Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static uint PJWHash(string str)
        {
            int BitsInUnignedInt = (int)(sizeof(int) * 8);
            int ThreeQuarters = (int)((BitsInUnignedInt * 3) / 4);
            int OneEighth = (int)(BitsInUnignedInt / 8);

            uint HighBits = 0xFFFFFFFF << (BitsInUnignedInt - OneEighth);
            uint hash = 0;
            uint test = 0;

            for (int i = 0; i < str.Length; i++)
            {
                hash = (hash << OneEighth) + str[i];
                if ((test = hash & HighBits) != 0)
                {
                    hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
                }
            }

            return (hash & 0x7FFFFFFF);
        }

        //public const int ELFHASH_SEED = 4026531840;

        /// <summary>
        /// ELF Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static uint ELFHash(string str)
        {
            uint hash = 0;
            uint x = 0;

            for (int i = 0; i < str.Length; i++)
            {
                hash = (hash << 4) + str[i];
                if ((x = hash & (uint)0xF0000000L) != 0)
                {
                    hash ^= (x >> 24);
                    hash &= ~x;
                }
            }

            return (hash & 0x7FFFFFFF);
        }

        /// <summary>
        /// BKDR Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static int BKDRHash(string str)
        {
            int seed = 131; // 31 131 1313 13131 131313 etc..
            int hash = 0;

            for (int i = 0; i < str.Length; i++)
            {
                hash = hash * seed + str[i];
            }

            return (hash & 0x7FFFFFFF);
        }

        /// <summary>
        /// SDBM Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static int SDBMHash(string str)
        {
            int hash = 0;

            for (int i = 0; i < str.Length; i++)
            {
                hash = str[i] + (hash << 6) + (hash << 16) - hash;
            }

            return (hash & 0x7FFFFFFF);
        }

        /// <summary>
        /// DJB Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static int DJBHash(string str)
        {
            int hash = 5381;

            for (int i = 0; i < str.Length; i++)
            {
                hash += (hash << 5) + str[i];
            }

            return (hash & 0x7FFFFFFF);
        }

        /// <summary>
        /// AP Hash Function
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static int APHash(string str)
        {
            int hash = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if ((i & 1) == 0)
                {
                    hash ^= ((hash << 7) ^ str[i] ^ (hash >> 3));
                }
                else
                {
                    hash ^= (~((hash << 11) ^ str[i] ^ (hash >> 5)));
                }
            }
            return (hash & 0x7FFFFFFF);
        }

        /// <summary>
        /// MPQHash
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static long MPQHash(string str)
        {
            return MPQHash(str, 0);
        }

        /// <summary>
        /// MPQHash
        /// </summary>
        /// <param name="str"></param>
        /// <param name="hashType"></param>
        /// <returns></returns>
        public static long MPQHash(string str, long hashType)
        {
            long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;
            int ch;
            for (int i = 0; i < str.Length; i++)
            {
                ch = str[i].ToString().ToUpper()[0];
                seed1 = (hashType << 8) + ch ^ (seed1 + seed2);
                seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
            }
            return seed1;
        }

    }
}
